package cn.ben3.addondsl.model;

import org.springframework.roo.classpath.PhysicalTypeMetadata;
import org.springframework.roo.classpath.details.*;
import org.springframework.roo.classpath.details.annotations.AnnotatedJavaType;
import org.springframework.roo.classpath.details.annotations.AnnotationMetadataBuilder;
import org.springframework.roo.model.JavaSymbolName;
import org.springframework.roo.model.JavaType;

import java.util.ArrayList;
import java.util.List;
import java.util.regex.Matcher;
import java.util.regex.Pattern;


public class ITD {
    private String identifier;
    private JavaType aspectName;
    private PhysicalTypeMetadata PhysicalTypeMetadata;
    private ItdTypeDetailsBuilder builder;
    private ClassOrInterfaceTypeDetails governorTypeDetails;

    /*static Util methods*/
    public static ITD getITD(String identifier,
                             JavaType aspectName,
                             PhysicalTypeMetadata governorPhysicalTypeMetadata ,
                             ItdTypeDetailsBuilder builder,ClassOrInterfaceTypeDetails governorTypeDetails){
        return new ITD(identifier, aspectName, governorPhysicalTypeMetadata,builder, governorTypeDetails);
    }


    /*AnnotationMetadataBuilders*/
    public static List<AnnotationMetadataBuilder> AMBs(AnnotationBuilder...anos) {
        List<AnnotationMetadataBuilder> ambs = new ArrayList<AnnotationMetadataBuilder>();
        for (AnnotationBuilder ano : anos) {
            ambs.add(ano.builder());
        }
        return ambs;
    }
     /*AnnotatedJavaTypes*/
    public static List<AnnotatedJavaType> AJTs(AnnotatedJavaType ...ajts) {
        List<AnnotatedJavaType> ambs = new ArrayList<AnnotatedJavaType>();
        for (AnnotatedJavaType ano : ajts) {
            ambs.add(ano);
        }
        return ambs;
    }
    /*JavaTypes*/
    public static List<JavaType> JTs(JavaType ...jts) {
        List<JavaType> ambs = new ArrayList<JavaType>();
        for (JavaType ano : jts) {
            ambs.add(ano);
        }
        return ambs;
    }
    /*JavaSymbolNames*/
    public static List<JavaSymbolName> JSNs(JavaSymbolName ...jts) {
        List<JavaSymbolName> ambs = new ArrayList<JavaSymbolName>();
        for (JavaSymbolName ano : jts) {
            ambs.add(ano);
        }
        return ambs;
    }

    /*no space in var  replace in sequence eg: varStr("hello $name$,  you are a $role$","Jimmy","teacher") */
    public String varStr(String line,String ...vars) {
        Pattern pattern = Pattern.compile("\\$[\\d|\\w]+\\$");

        Matcher matcher = pattern.matcher(line);
        StringBuffer sbr = new StringBuffer();
        int pos=0;
        while (matcher.find()) {
            System.out.println(true);
            matcher.appendReplacement(sbr, vars[pos]);
            pos++;
        }
        matcher.appendTail(sbr);
        return sbr.toString();
    }
    /*get a FieldBuilder instance*/
    public static FieldBuilder fld(final String declaredbyMetadataId,
                          final int modifier,
                          final List<AnnotationMetadataBuilder> annotations,
                          final String fieldName, final JavaType fieldType) {
        final JavaSymbolName field = new JavaSymbolName(fieldName);
        return new FieldBuilder(declaredbyMetadataId,modifier,annotations,field,fieldType);
    }

    public static FieldBuilder fld(final String declaredbyMetadataId,
                          final int modifier,
                          final List<AnnotationMetadataBuilder> annotations,
                          final String fieldName, final String fieldType){
        final JavaSymbolName field = new JavaSymbolName(fieldName);
        final JavaType type = new JavaType(fieldType);
        return new FieldBuilder(declaredbyMetadataId,modifier,annotations,field,type);
    }
    public static FieldBuilder fld(final String declaredbyMetadataId,
                            final int modifier,
                            final String fieldName, final String fieldType){
        final JavaSymbolName field = new JavaSymbolName(fieldName);
        final JavaType type = new JavaType(fieldType);
        final List<AnnotationMetadataBuilder> annotations=new ArrayList<AnnotationMetadataBuilder>();
        return new FieldBuilder(declaredbyMetadataId,modifier,annotations,field,type);
    }
    public static FieldBuilder fld(final String declaredbyMetadataId,
                            final int modifier,
                            final String fieldName, final JavaType fieldType){
        final JavaSymbolName field = new JavaSymbolName(fieldName);

        final List<AnnotationMetadataBuilder> annotations=new ArrayList<AnnotationMetadataBuilder>();
        return new FieldBuilder(declaredbyMetadataId,modifier,annotations,field,fieldType);
    }

    /*get a MethodBuilder instance*/
    public static MethodBuilder med(final String declaredbyMetadataId,
                          final int modifier,
                          final JavaSymbolName methodName,JavaType returnType,MethodBodyBuilder bodyBuilder){
        return new MethodBuilder(declaredbyMetadataId, modifier, methodName, returnType,bodyBuilder.builder());
    }
    public static MethodBuilder med(final String declaredbyMetadataId,
                          final int modifier,
                          final String methodName,String returnType,MethodBodyBuilder bodyBuilder) {
        JavaSymbolName name = new JavaSymbolName(methodName);
        JavaType type = new JavaType(returnType);
        return new MethodBuilder(declaredbyMetadataId, modifier, name, type, bodyBuilder.builder());
    }
    public static MethodBuilder med(final String declaredbyMetadataId,
                          final int modifier,
                          final String methodName,JavaType returnType,MethodBodyBuilder bodyBuilder) {
        JavaSymbolName name = new JavaSymbolName(methodName);
        return new MethodBuilder(declaredbyMetadataId, modifier, name, returnType, bodyBuilder.builder());
    }
    /*get a AnnotationBuilder instance*/
    public static AnnotationBuilder ano(final String javaType){
        return new AnnotationBuilder(new JavaType(javaType));
    }
    public static AnnotationBuilder ano(final JavaType javaType){
        return new AnnotationBuilder(javaType);
    }
    public static AnnotationBuilder ano(final Class<?> javaType){
        return new AnnotationBuilder(new JavaType(javaType));
    }
    
    /*get a AnnotatedJavaTypeBuilder instance*/
    public static AnnotatedJavaTypeBuilder ajtb(final String javaType){
        return new AnnotatedJavaTypeBuilder(new JavaType(javaType));
    }
    public static AnnotatedJavaTypeBuilder ajtb(final JavaType javaType){
        return new AnnotatedJavaTypeBuilder(javaType);
    }
    public static AnnotatedJavaTypeBuilder ajtb(final Class<?> javaType){
        return new AnnotatedJavaTypeBuilder(new JavaType(javaType));
    }
    /*get a JavaType instance*/
    public static JavaType jt(final Class<?> javaType){
        return new JavaType(javaType);
    }
    public static JavaType jt(final String javaType){
        return new JavaType(javaType);
    }
     /*get a AnnotatedJavaTypeBuilder instance*/
     public static JavaSymbolName jsn(final String symbolName){
         return new JavaSymbolName(symbolName);
     }
    /*get a MethodBodyBuilder instance*/
    public static MethodBodyBuilder mbody(){
        return new MethodBodyBuilder();
    }
    /*end- static Util*/
    public ITD() {
    }

    private ITD(String identifier, JavaType aspectName, PhysicalTypeMetadata governorPhysicalTypeMetadata ,ItdTypeDetailsBuilder builder,ClassOrInterfaceTypeDetails governorTypeDetails) {

        this.aspectName = aspectName;
        this.identifier = identifier;
        this.PhysicalTypeMetadata = governorPhysicalTypeMetadata;
        this.builder=builder;
        this.governorTypeDetails=governorTypeDetails;
    }

    /*add a FieldBuilder to the ITD instance*/
    public boolean adF(FieldBuilder fld) {
       return this.builder.addField(fld.build());
    }
    /*add a MethodBuilder to the ITD instance*/
    public boolean adM(MethodBuilder med){

        final MethodMetadata method = methodExists(med.getMethodName(), med.getParameterTypes());
        if (method != null) {
            // If it already exists, just return the method and omit its generation via the ITD
            return this.builder.addMethod(method) ;
        }
        return this.builder.addMethod(med.build());
    }
     /*get the ItdTypeDetails instance */
    public ItdTypeDetails build(){
        return this.builder.build();
    }
    /*defender*/
    private MethodMetadata methodExists(JavaSymbolName methodName, List<AnnotatedJavaType> paramTypes) {
        // We have no access to method parameter information, so we scan by name alone and treat any match as authoritative
        // We do not scan the superclass, as the caller is expected to know we'll only scan the current class
        for (MethodMetadata method : governorTypeDetails.getDeclaredMethods()) {
            if (method.getMethodName().equals(methodName) && method.getParameterTypes().equals(paramTypes)) {
                // Found a method of the expected name; we won't check method parameters though
                return method;
            }
        }
        return null;
    }


}

